package com.hc.aop;


import cn.dev33.satoken.stp.StpUtil;
import com.hc.Operation;
import com.hc.bean.SysLogBean;
import com.hc.mapper.SysLogMapper;
import com.hc.util.JsonResponse;
import jakarta.servlet.http.HttpServletRequest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import java.lang.reflect.Method;
import java.util.Arrays;

@Component
@Aspect //添加了该注解的类就是切面类
public class LogAspect {

    //定义切入点，关注控制器层所有方法的执行    ..*表示 controller包或子包中的所有类   ..*.* 后面的.*表示类中的所有方法  (..)表示方法的参数为任意个
    @Pointcut("execution( * com.hc.controller..*.*(..))")
    public  void pointCut(){}


    //定义通知 前置通知
  /*  @Before("pointCut()")
    public  void before(){
          logger.debug("befor前置通知执行了");
    }

    //异常通知
    @AfterThrowing("pointCut()")
    public void AfterThrowing(){
          logger.debug("AfterThrowing异常通知执行了,方法抛出了异常");

    }

    //返回通知
    @AfterReturning("pointCut()")
    public void AfterReturning(){
          logger.debug("AfterReturning返回通知执行了,方法未抛出异常");

    }

    //后置通知 (最终通知，不管方法是否出现异常，始终会执行的)
    @After("pointCut()")
    public void after(){
          logger.debug("after后置通知执行了");

    }
*/

    //环绕通知,注：环绕通知的方法参数需要添加一个ProceedingJoinPoint这个类型的参数,
    //ProceedingJoinPoint代表切入点的相关信息

    @Autowired
    SysLogMapper sysLogMapper ;

    Logger logger = LoggerFactory.getLogger(this.getClass().getName());
    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint){
        SysLogBean sysLogBean = new SysLogBean();


        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        //从获取RequestAttributes中获取HttpServletRequest的信息
        HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);






          logger.debug("被代理对象的类名:"+joinPoint.getTarget().getClass().getName());
          logger.debug("目标对象的方法名:"+joinPoint.getSignature().getName());
          logger.debug("目标对象的参数(实参)"+ Arrays.toString( joinPoint.getArgs()));
          logger.debug("客户端的IP:"+request.getRemoteAddr());

       // Signature singature =  joinPoint.getSignature();

        Class targClass =  joinPoint.getTarget().getClass();
        Method[] methods =  targClass.getDeclaredMethods();
        for(Method m :methods){
            if (m.getName().equals(joinPoint.getSignature().getName())){//当类中的方法名与被调用方法一致时
                //拿方法上存在的注解
                if (m.isAnnotationPresent(Operation.class)){
                    Operation operation =  m.getAnnotation(Operation.class);
                      logger.debug("方法上的option注解为:"+operation.value());
                    sysLogBean.setOperation(operation.value());
                }

            }
        }





        try {
              logger.debug("前置通知");



            long bTime = System.currentTimeMillis();
           Object methodReturnVal =   joinPoint.proceed();//执行processd方法才会去调用目标对象相应的方法
              logger.debug("返回通知,方法返回值为:"+methodReturnVal);
            long eTime = System.currentTimeMillis();
              logger.debug("执行时长:"+(eTime-bTime));



            try {
                sysLogBean.setIp(request.getRemoteAddr());
                sysLogBean.setMethod(joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()");
                sysLogBean.setParams(Arrays.toString(joinPoint.getArgs()));
                sysLogBean.setUsername("admin");
                sysLogBean.setTime((int) (eTime - bTime));
                sysLogMapper.insertData(sysLogBean);
            }catch ( Exception e){
                logger.error("记录操作日志失败",e);
               // e.printStackTrace();
            }

            return  methodReturnVal;//把控制器层方法返回值返回给前置
        }catch (Throwable t){
            logger.error("调用控制器的方法出错了",t);
              logger.debug("异常通知");
            JsonResponse jsonResponse = new JsonResponse();
            jsonResponse.setCode(1);
            jsonResponse.setMsg("在切面类中捕获到了控制器中抛出的异常"+t.getMessage());
            return  jsonResponse;
        }finally {
              logger.debug("最终通知");
        }

    }

}
